package org.lex.jdbc.util;

import java.io.File;
import java.io.IOException;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.lex.io.utils.FileUtils;
import org.lex.jdbc.MyDriverManager;
import org.lex.lang.AggregateException;
import org.lex.lang.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DriverUtils {
	private static final Logger log = LoggerFactory.getLogger(DriverUtils.class);

	/**
	 * @param from
	 *            a standard JAR file, or a folder contains JAR file
	 * @return
	 * @throws IOException
	 */
	public static Collection<Class<?>> loadDrivers(Collection<Class<?>> drivers, File... from) throws AggregateException {
		Collection<File> allFiles = new LinkedList<File>();
		SuffixFileFilter jarFilter = new SuffixFileFilter(".jar", IOCase.INSENSITIVE);
		for (File file : from) {
			if (file.isFile()) {
				allFiles.add(file);
			} else if (file.isDirectory()) {
				Collection<File> files = FileUtils.listFiles(file, jarFilter);
				allFiles.addAll(files);
			}
		}

		Collection<Class<?>> allDrivers = drivers;
		List<Exception> exceps = new ArrayList<Exception>();
		for (File file : allFiles) {
			log.trace("loading: " + file);
			try {
				Collection<Class<?>> driver = loadDrivers0(file);
				allDrivers.addAll(driver);
			} catch (IOException e) {
				exceps.add(e);
			}
		}
		if (false == exceps.isEmpty()) {
			throw new AggregateException(exceps);
		}
		return allDrivers;
	}

	/**
	 * @param jar
	 *            a standard JAR file
	 * @return
	 * @throws IOException
	 */
	private static Collection<Class<?>> loadDrivers0(File jar) throws IOException {
		Collection<Class<?>> drivers = new LinkedList<Class<?>>();
		Collection<String> classNames = ClassUtils.getClassNames(jar);
		Class<Driver> driverClz = Driver.class;
		ClassLoader loader = ClassUtils.loadJar(jar);
		for (String name : classNames) {
			try {
				Class<?> clz = Class.forName(name, true, loader);
				if (null != clz && driverClz.isAssignableFrom(clz)) {
					log.info("Found JDBC Driver: " + clz.getName());
					drivers.add(clz);
					Driver ins = (Driver) clz.newInstance();
					MyDriverManager.registerDriver(ins);
				}
				log.trace("load class [" + name + "] success");
			} catch (Throwable e) {
				String msg = null;
				if (null != e.getCause()) {
					msg = e.getCause().getMessage();
				} else {
					msg = e.getMessage();
				}
				log.info("load class [" + name + "] failed: cause=" + msg);
			}
		}
		return drivers;
	}
}
